/**
*    Copyright 2011, Big Switch Networks, Inc.
*    Originally created by David Erickson, Stanford University
*
*    Licensed under the Apache License, Version 2.0 (the "License"); you may
*    not use this file except in compliance with the License. You may obtain
*    a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0
*
*    Unless required by applicable law or agreed to in writing, software
*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
*    License for the specific language governing permissions and limitations
*    under the License.
**/

package net.floodlightcontroller.core;

import java.io.IOException;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import net.floodlightcontroller.core.IFloodlightProviderService.Role;
import net.floodlightcontroller.core.internal.Controller;
import net.floodlightcontroller.debugcounter.IDebugCounterService;
import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
import net.floodlightcontroller.threadpool.IThreadPoolService;
import net.floodlightcontroller.util.OrderedCollection;

import org.jboss.netty.channel.Channel;
import org.openflow.protocol.OFFeaturesReply;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPortStatus;
import org.openflow.protocol.OFStatisticsReply;
import org.openflow.protocol.OFStatisticsRequest;
import org.openflow.protocol.statistics.OFDescriptionStatistics;
import org.openflow.protocol.statistics.OFStatistics;

/**
 *
 *
 * @author David Erickson (daviderickson@cs.stanford.edu)
 */
public interface IOFSwitch {
	// Attribute keys
	public static final String SWITCH_DESCRIPTION_FUTURE = "DescriptionFuture";
	public static final String SWITCH_SUPPORTS_NX_ROLE = "supportsNxRole";
	public static final String SWITCH_IS_CORE_SWITCH = "isCoreSwitch";
	public static final String PROP_FASTWILDCARDS = "FastWildcards";
	public static final String PROP_REQUIRES_L3_MATCH = "requiresL3Match";
	public static final String PROP_SUPPORTS_OFPP_TABLE = "supportsOfppTable";
	public static final String PROP_SUPPORTS_OFPP_FLOOD = "supportsOfppFlood";
	public static final String PROP_SUPPORTS_NETMASK_TBL = "supportsNetmaskTbl";

	public enum OFPortType {
		NORMAL("normal"), // normal port (default)
		TUNNEL("tunnel"), // tunnel port 隧道端口
		UPLINK("uplink"), // uplink port (on a virtual switch) 上行端口
		MANAGEMENT("management"), // for in-band management 对于允许通过的管理
		TUNNEL_LOOPBACK("tunnel-loopback");// 隧道回路

		private final String value;

		OFPortType(String v) {
			value = v;
		}

		@Override
		public String toString() {
			return value;
		}

		public static OFPortType fromString(String str) {
			for (OFPortType m : OFPortType.values()) {
				if (m.value.equals(str)) {
					return m;
				}
			}
			return OFPortType.NORMAL;
		}
	}

	/**
	 * Describes a change of an open flow port
	 * 描述一个open flow端口的变化
	 */
	public static class PortChangeEvent {
		public final ImmutablePort port;
		public final PortChangeType type;

		/**
		 * @param port
		 * @param type
		 */
		public PortChangeEvent(ImmutablePort port, PortChangeType type) {
			this.port = port;
			this.type = type;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#hashCode()
		 */
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((port == null) ? 0 : port.hashCode());
			result = prime * result + ((type == null) ? 0 : type.hashCode());
			return result;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#equals(java.lang.Object)
		 */
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			PortChangeEvent other = (PortChangeEvent) obj;
			if (port == null) {
				if (other.port != null)
					return false;
			} else if (!port.equals(other.port))
				return false;
			if (type != other.type)
				return false;
			return true;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#toString()
		 */
		@Override
		public String toString() {
			return "[" + type + " " + port.toBriefString() + "]";
		}
	}

	/**
	 * the type of change that happened to an open flow port
	 * 端口改变的类型
	 */
	public enum PortChangeType {
		ADD, OTHER_UPDATE, DELETE, UP, DOWN,
	}

	/**
	 * Set IFloodlightProviderService for this switch instance Called
	 * immediately after instantiation
	 *
	 * @param controller
	 */
	public void setFloodlightProvider(Controller controller);

	/**
	 * Set IThreadPoolService for this switch instance Called immediately after
	 * instantiation
	 *
	 * @param threadPool
	 */
	public void setThreadPoolService(IThreadPoolService threadPool);

	/**
	 * Set debug counter service for per-switch counters Called immediately
	 * after instantiation
	 * 
	 * @param debugCounters
	 * @throws CounterException
	 */
	public void setDebugCounterService(IDebugCounterService debugCounters) throws CounterException;

	/**
	 * Set the netty Channel this switch instance is associated with Called
	 * immediately after instantiation
	 *
	 * @param channel
	 */
	public void setChannel(Channel channel);

	/**
	 * Called when OFMessage enters pipeline. Returning true cause the message
	 * to be dropped.
	 * 
	 * @param ofm
	 * @return
	 */
	public boolean inputThrottled(OFMessage ofm);

	/**
	 * Return if the switch is currently overloaded. The definition of overload
	 * refers to excessive traffic in the control path, namely a high packet in
	 * rate.
	 * 
	 * @return
	 */
	boolean isOverloaded();

	/**
	 * Write OFMessage to the output stream, subject to switch rate limiting.
	 * The message will be handed to the floodlightProvider for possible
	 * filtering and processing by message listeners
	 * 
	 * @param msg
	 * @param cntx
	 * @throws IOException
	 */
	public void writeThrottled(OFMessage msg, FloodlightContext cntx) throws IOException;

	/**
	 * Writes the list of messages to the output stream, subject to rate
	 * limiting. The message will be handed to the floodlightProvider for
	 * possible filtering and processing by message listeners.
	 * 
	 * @param msglist
	 * @param bc
	 * @throws IOException
	 */
	void writeThrottled(List<OFMessage> msglist, FloodlightContext bc) throws IOException;

	/**
	 * Writes to the OFMessage to the output stream, bypassing rate limiting.
	 * The message will be handed to the floodlightProvider for possible
	 * filtering and processing by message listeners
	 * 
	 * @param m
	 * @param bc
	 * @throws IOException
	 */
	public void write(OFMessage m, FloodlightContext bc) throws IOException;

	/**
	 * Writes the list of messages to the output stream, bypassing rate
	 * limiting. The message will be handed to the floodlightProvider for
	 * possible filtering and processing by message listeners.
	 * 
	 * @param msglist
	 * @param bc
	 * @throws IOException
	 */
	public void write(List<OFMessage> msglist, FloodlightContext bc) throws IOException;

	/**
	 *
	 * @throws IOException
	 */
	public void disconnectOutputStream();

	/**
	 * Returns switch features from features Reply
	 * 
	 * @return
	 */
	public int getBuffers();

	public int getActions();

	public int getCapabilities();

	public byte getTables();

	/**
	 * @return a copy of the description statistics for this switch
	 */
	public OFDescriptionStatistics getDescriptionStatistics();

	/**
	 * Set the OFFeaturesReply message returned by the switch during initial
	 * handshake.
	 * 
	 * @param featuresReply
	 */
	public void setFeaturesReply(OFFeaturesReply featuresReply);

	/**
	 * Get list of all enabled ports. This will typically be different from the
	 * list of ports in the OFFeaturesReply, since that one is a static snapshot
	 * of the ports at the time the switch connected to the controller whereas
	 * this port list also reflects the port status messages that have been
	 * received.
	 * 
	 * @return Unmodifiable list of ports not backed by the underlying
	 *         collection
	 */
	public Collection<ImmutablePort> getEnabledPorts();

	/**
	 * Get list of the port numbers of all enabled ports. This will typically be
	 * different from the list of ports in the OFFeaturesReply, since that one
	 * is a static snapshot of the ports at the time the switch connected to the
	 * controller whereas this port list also reflects the port status messages
	 * that have been received.
	 * 
	 * @return Unmodifiable list of ports not backed by the underlying
	 *         collection
	 */
	public Collection<Short> getEnabledPortNumbers();

	/**
	 * Retrieve the port object by the port number. The port object is the one
	 * that reflects the port status updates that have been received, not the
	 * one from the features reply.
	 * 
	 * @param portNumber
	 * @return port object
	 */
	public ImmutablePort getPort(short portNumber);

	/**
	 * Retrieve the port object by the port name. The port object is the one
	 * that reflects the port status updates that have been received, not the
	 * one from the features reply. Port names are case insentive
	 * 
	 * @param portName
	 * @return port object
	 */
	public ImmutablePort getPort(String portName);

	/**
	 * Add or modify a switch port. This is called by the core controller code
	 * in response to a OFPortStatus message. It should not typically be called
	 * by other floodlight applications.
	 *
	 * OFPPR_MODIFY and OFPPR_ADD will be treated as equivalent. The OpenFlow
	 * spec is not clear on whether portNames are portNumbers are considered
	 * authoritative identifiers. We treat portNames <-> portNumber mappings as
	 * fixed. If they change, we delete all previous conflicting ports and add
	 * all new ports.
	 *
	 * @param ps
	 *            the port status message
	 * @return the ordered Collection of changes "applied" to the old ports of
	 *         the switch according to the PortStatus message. A single
	 *         PortStatus message can result in multiple changes. If portName
	 *         <-> portNumber mappings have changed, the iteration order ensures
	 *         that delete events for old conflicting appear before before
	 *         events adding new ports
	 */
	public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps);

	/**
	 * Get list of all ports. This will typically be different from the list of
	 * ports in the OFFeaturesReply, since that one is a static snapshot of the
	 * ports at the time the switch connected to the controller whereas this
	 * port list also reflects the port status messages that have been received.
	 * 
	 * @return Unmodifiable list of ports
	 */
	public Collection<ImmutablePort> getPorts();

	/**
	 * @param portNumber
	 * @return Whether a port is enabled per latest port status message (not
	 *         configured down nor link down nor in spanning tree blocking
	 *         state)
	 */
	public boolean portEnabled(short portNumber);

	/**
	 * @param portNumber
	 * @return Whether a port is enabled per latest port status message (not
	 *         configured down nor link down nor in spanning tree blocking
	 *         state)
	 */
	public boolean portEnabled(String portName);

	/**
	 * Compute the changes that would be required to replace the old ports of
	 * this switch with the new ports
	 * 
	 * @param ports
	 *            new ports to set
	 * @return the ordered collection of changes "applied" to the old ports of
	 *         the switch in order to set them to the new set. If portName
	 *         <-> portNumber mappings have changed, the iteration order ensures
	 *         that delete events for old conflicting appear before before
	 *         events adding new ports
	 */
	public OrderedCollection<PortChangeEvent> comparePorts(Collection<ImmutablePort> ports);

	/**
	 * Replace the ports of this switch with the given ports.
	 * 
	 * @param ports
	 *            new ports to set
	 * @return the ordered collection of changes "applied" to the old ports of
	 *         the switch in order to set them to the new set. If portName
	 *         <-> portNumber mappings have changed, the iteration order ensures
	 *         that delete events for old conflicting appear before before
	 *         events adding new ports
	 */
	public OrderedCollection<PortChangeEvent> setPorts(Collection<ImmutablePort> ports);

	/**
	 * Get the datapathId of the switch
	 * 
	 * @return
	 */
	public long getId();

	/**
	 * Get a string version of the ID for this switch
	 * 
	 * @return
	 */
	public String getStringId();

	/**
	 * Get the IP Address for the switch
	 * 
	 * @return the inet address
	 */
	public SocketAddress getInetAddress();

	/**
	 * Retrieves attributes of this switch
	 * 
	 * @return
	 */
	public Map<Object, Object> getAttributes();

	/**
	 * Retrieves the date the switch connected to this controller
	 * 
	 * @return the date
	 */
	public Date getConnectedSince();

	/**
	 * Returns the next available transaction id
	 * 
	 * @return
	 */
	public int getNextTransactionId();

	/**
	 * Returns a Future object that can be used to retrieve the asynchronous
	 * OFStatisticsReply when it is available.
	 *
	 * @param request
	 *            statistics request
	 * @return Future object wrapping OFStatisticsReply
	 * @throws IOException
	 */
	public Future<List<OFStatistics>> queryStatistics(OFStatisticsRequest request)

			throws IOException;

	/**
	 * Returns a Future object that can be used to retrieve the asynchronous
	 * OFStatisticsReply when it is available.
	 *
	 * @param request
	 *            statistics request
	 * @return Future object wrapping OFStatisticsReply
	 * @throws IOException
	 */
	public Future<OFFeaturesReply> querySwitchFeaturesReply() throws IOException;

	/**
	 * Deliver the featuresReply future reply
	 * 
	 * @param reply
	 *            the reply to deliver
	 */
	void deliverOFFeaturesReply(OFMessage reply);

	/*
	 * Cancel features reply with a specific transction ID
	 * 
	 * @param transactionId the transaction ID
	 */
	public void cancelFeaturesReply(int transactionId);

	/**
	 * Check if the switch is connected to this controller. Whether a switch is
	 * connected is independent of whether the switch is active
	 * 
	 * @return whether the switch is still disconnected
	 */
	public boolean isConnected();

	/**
	 * Check if the switch is active. I.e., the switch is connected to this
	 * controller and is in master role
	 * 
	 * @return
	 */
	public boolean isActive();

	/**
	 * Set whether the switch is connected
	 * 
	 * @param connected
	 *            whether the switch is connected
	 */
	public void setConnected(boolean connected);

	/**
	 * Get the current role of the controller for the switch
	 * 
	 * @return the role of the controller
	 */
	public Role getHARole();

	/**
	 * Set switch's HA role to role. The haRoleReplyReceived indicates if a
	 * reply was received from the switch (error replies excluded).
	 *
	 * If role is null, the switch should close the channel connection.
	 *
	 * @param role
	 * @param haRoleReplyReceived
	 */
	public void setHARole(Role role);

	/**
	 * Deliver the statistics future reply
	 * 
	 * @param reply
	 *            the reply to deliver
	 */
	public void deliverStatisticsReply(OFStatisticsReply reply);

	/**
	 * Cancel the statistics reply with the given transaction ID
	 * 
	 * @param transactionId
	 *            the transaction ID
	 */
	public void cancelStatisticsReply(int transactionId);

	/**
	 * Cancel all statistics replies
	 */
	public void cancelAllStatisticsReplies();

	/**
	 * Checks if a specific switch property exists for this switch
	 * 
	 * @param name
	 *            name of property
	 * @return value for name
	 */
	boolean hasAttribute(String name);

	/**
	 * Set properties for switch specific behavior
	 * 
	 * @param name
	 *            name of property
	 * @return value for name
	 */
	Object getAttribute(String name);

	/**
	 * Check if the given attribute is present and if so whether it is equal to
	 * "other"
	 * 
	 * @param name
	 *            the name of the attribute to check
	 * @param other
	 *            the object to compare the attribute against.
	 * @return true iff the specified attribute is set and equals() the given
	 *         other object.
	 */
	boolean attributeEquals(String name, Object other);

	/**
	 * Set properties for switch specific behavior
	 * 
	 * @param name
	 *            name of property
	 * @param value
	 *            value for name
	 */
	void setAttribute(String name, Object value);

	/**
	 * Set properties for switch specific behavior
	 * 
	 * @param name
	 *            name of property
	 * @return current value for name or null (if not present)
	 */
	Object removeAttribute(String name);

	/**
	 * Clear all flowmods on this switch
	 */
	public void clearAllFlowMods();

	/**
	 * Update broadcast cache
	 * 
	 * @param data
	 * @return true if there is a cache hit false if there is no cache hit.
	 */
	public boolean updateBroadcastCache(Long entry, Short port);

	/**
	 * Get the portBroadcastCacheHits
	 * 
	 * @return
	 */
	public Map<Short, Long> getPortBroadcastHits();

	/**
	 * Send a flow statistics request to the switch. This call returns after
	 * sending the stats. request to the switch.
	 * 
	 * @param request
	 *            flow statistics request message
	 * @param xid
	 *            transaction id, must be obtained by using the getXid() API.
	 * @param caller
	 *            the caller of the API. receive() callback of this caller would
	 *            be called when the reply from the switch is received.
	 * @return the transaction id for the message sent to the switch. The
	 *         transaction id can be used to match the response with the
	 *         request. Note that the transaction id is unique only within the
	 *         scope of this switch.
	 * @throws IOException
	 */
	public void sendStatsQuery(OFStatisticsRequest request, int xid, IOFMessageListener caller) throws IOException;

	/**
	 * Flush all flows queued for this switch in the current thread. NOTE: The
	 * contract is limited to the current thread
	 */
	public void flush();

	/***********************************************
	 * The following method can be overridden by specific types of switches
	 ***********************************************
	 */

	/**
	 * Set the SwitchProperties based on it's description
	 * 
	 * @param description
	 */
	public void setSwitchProperties(OFDescriptionStatistics description);

	/**
	 * Return the type of OFPort
	 * 
	 * @param port_num
	 * @return
	 */
	public OFPortType getPortType(short port_num);

	/**
	 * Can the port be turned on without forming a new loop?
	 * 
	 * @param port_num
	 * @return
	 */
	public boolean isFastPort(short port_num);

	/**
	 * Return whether write throttling is enabled on the switch
	 */
	public boolean isWriteThrottleEnabled();

	/**
	 * Set the flow table full flag in the switch
	 */
	public void setTableFull(boolean isFull);

	/**
	 * Set the suggested priority to use when installing access flows in this
	 * switch.
	 */
	public void setAccessFlowPriority(short prio);

	/**
	 * Set the suggested priority to use when installing core flows in this
	 * switch.
	 */
	public void setCoreFlowPriority(short prio);

	/**
	 * Get the suggested priority to use when installing access flows in this
	 * switch.
	 */
	public short getAccessFlowPriority();

	/**
	 * Get the suggested priority to use when installing core flows in this
	 * switch.
	 */
	public short getCoreFlowPriority();

	/**
	 * Start this switch driver's sub handshake. This might be a no-op but this
	 * method must be called at least once for the switch to be become ready.
	 * This method must only be called from the I/O thread
	 * 
	 * @throws SwitchDriverSubHandshakeAlreadyStarted
	 *             if the sub-handshake has already been started
	 */
	public void startDriverHandshake();

	/**
	 * Check if the sub-handshake for this switch driver has been completed.
	 * This method can only be called after startDriverHandshake()
	 *
	 * This methods must only be called from the I/O thread
	 * 
	 * @return true if the sub-handshake has been completed. False otherwise
	 * @throws SwitchDriverSubHandshakeNotStarted
	 *             if startDriverHandshake() has not been called yet.
	 */
	public boolean isDriverHandshakeComplete();

	/**
	 * Pass the given OFMessage to the driver as part of this driver's
	 * sub-handshake. Must not be called after the handshake has been completed
	 * This methods must only be called from the I/O thread
	 * 
	 * @param m
	 *            The message that the driver should process
	 * @throws SwitchDriverSubHandshakeCompleted
	 *             if isDriverHandshake() returns false before this method call
	 * @throws SwitchDriverSubHandshakeNotStarted
	 *             if startDriverHandshake() has not been called yet.
	 */
	public void processDriverHandshakeMessage(OFMessage m);
}
